package net.jgrm.redisadvanced.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.jgrm.redisadvanced.config.RedisAdvancedProperties;
import net.jgrm.redisadvanced.service.RedisStringService;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * Redis String类型操作服务实现类
 */
@Slf4j
//@Service
public class RedisStringServiceImpl implements RedisStringService {

    //    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    //    @Autowired
    private RedisAdvancedProperties redisAdvancedProperties;

    //    @Autowired
    private ObjectMapper objectMapper;

    public RedisStringServiceImpl(RedisTemplate<String, Object> redisTemplate, RedisAdvancedProperties redisAdvancedProperties, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.redisAdvancedProperties = redisAdvancedProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 添加前缀到键名
     */
    private String addPrefix(String key) {
        return redisAdvancedProperties.getKeyPrefix() + key;
    }

    @Override
    public void set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(addPrefix(key), value);
            log.debug("String set successful: key={}", key);
        } catch (Exception e) {
            log.error("String set error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set operation failed", e);
        }
    }

    @Override
    public void set(String key, Object value, long expireTime, TimeUnit timeUnit) {
        try {
            redisTemplate.opsForValue().set(addPrefix(key), value, expireTime, timeUnit);
            log.debug("String set with expire successful: key={}, expireTime={}, timeUnit={}", key, expireTime, timeUnit);
        } catch (Exception e) {
            log.error("String set with expire error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis set with expire operation failed", e);
        }
    }

    @Override
    public <T> T get(String key, Class<T> clazz) {
        try {
            Object value = redisTemplate.opsForValue().get(addPrefix(key));
            log.debug("String get successful: key={}", key);
            if (value == null) {
                return null;
            }
            // 如果是目标类型直接返回，否则尝试转换
            if (clazz.isInstance(value)) {
                return clazz.cast(value);
            } else {
                // 处理序列化后可能的类型转换问题
                return objectMapper.convertValue(value, clazz);
            }
        } catch (Exception e) {
            log.error("String get error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis get operation failed", e);
        }
    }

    @Override
    public Boolean setIfAbsent(String key, Object value) {
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(addPrefix(key), value);
            log.debug("String setIfAbsent successful: key={}, result={}", key, result);
            return result;
        } catch (Exception e) {
            log.error("String setIfAbsent error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis setIfAbsent operation failed", e);
        }
    }

    @Override
    public Boolean setIfAbsent(String key, Object value, long expireTime, TimeUnit timeUnit) {
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(addPrefix(key), value, expireTime, timeUnit);
            log.debug("String setIfAbsent with expire successful: key={}, result={}", key, result);
            return result;
        } catch (Exception e) {
            log.error("String setIfAbsent with expire error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis setIfAbsent with expire operation failed", e);
        }
    }


    @Override
    public <T> T[] mGet(String[] keys, Class<T> clazz) {
        try {
            if (keys == null) {
                throw new IllegalArgumentException("Keys must not be null");
            }

            String[] prefixedKeys = new String[keys.length];
            for (int i = 0; i < keys.length; i++) {
                prefixedKeys[i] = addPrefix(keys[i]);
            }

            java.util.List<Object> values = redisTemplate.opsForValue().multiGet(Arrays.asList(prefixedKeys));
            log.debug("String mGet successful: keys={}", Arrays.toString(keys));

            @SuppressWarnings("unchecked")
            T[] result = (T[]) java.lang.reflect.Array.newInstance(clazz, values.size());
            for (int i = 0; i < values.size(); i++) {
                Object value = values.get(i);
                if (value != null) {
                    if (clazz.isInstance(value)) {
                        result[i] = clazz.cast(value);
                    } else {
                        result[i] = objectMapper.convertValue(value, clazz);
                    }
                }
            }
            return result;
        } catch (Exception e) {
            log.error("String mGet error: error={}", e.getMessage(), e);
            throw new RuntimeException("Redis mGet operation failed", e);
        }
    }

    @Override
    public Long increment(String key) {
        try {
            Long result = redisTemplate.opsForValue().increment(addPrefix(key));
            log.debug("String increment successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("String increment error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis increment operation failed", e);
        }
    }

    @Override
    public Long increment(String key, long delta) {
        try {
            Long result = redisTemplate.opsForValue().increment(addPrefix(key), delta);
            log.debug("String increment with delta successful: key={}, delta={}", key, delta);
            return result;
        } catch (Exception e) {
            log.error("String increment with delta error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis increment with delta operation failed", e);
        }
    }

    @Override
    public Long decrement(String key) {
        try {
            Long result = redisTemplate.opsForValue().decrement(addPrefix(key));
            log.debug("String decrement successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("String decrement error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis decrement operation failed", e);
        }
    }

    @Override
    public Long decrement(String key, long delta) {
        try {
            Long result = redisTemplate.opsForValue().decrement(addPrefix(key), delta);
            log.debug("String decrement with delta successful: key={}, delta={}", key, delta);
            return result;
        } catch (Exception e) {
            log.error("String decrement with delta error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis decrement with delta operation failed", e);
        }
    }

    @Override
    public Long size(String key) {
        try {
            Long result = redisTemplate.opsForValue().size(addPrefix(key));
            log.debug("String size successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("String size error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis size operation failed", e);
        }
    }

    @Override
    public Integer append(String key, String value) {
        try {
            Integer result = redisTemplate.opsForValue().append(addPrefix(key), value);
            log.debug("String append successful: key={}", key);
            return result;
        } catch (Exception e) {
            log.error("String append error: key={}, error={}", key, e.getMessage(), e);
            throw new RuntimeException("Redis append operation failed", e);
        }
    }
}